#pragma once

/* !关于堆开始地址选择的问题 */
/* 根据页表的结构，为了让TLB友好应当尽可能让地址空间紧凑*/
/* 使用sbrk(0)是比较好的选择 */


/* mmapheap本身不负责管理page		  信息*/




/* 申请方式是一次申请一大块(降低系统调用的开销) */
/* 默认情况下使用全局堆，若全局堆耗尽则尝试分配新堆取代全局堆, 若全局堆已经被取代(考虑并发)，则新堆留作自用，线程优先使用自己的堆 */
/* 为什么不直接mmap一个超巨型堆(比如64GB) 然后faa管理呢? */
/* 这种方式是不是有问题? 为什么不设置成一个标记位，特定线程去mmap(这样就是阻塞式了), 要确保整体是wait-free的*/

/* 不用管回收，这就是是个抽象OS内存管理层的玩意, 只要管理虚拟地址增长确定何时映射即可，其他由下层管理*/

/* 支持按页的方式free */

/* 比如lfsegfit管理页(decommit剩余部分或者标记剩余部分不可使用) */

/* 是否管理内存meta呢? */
/* 可以管理，这个heap按对象分配? */
/* 前面按..分配， 后面按...分配? */
/* 虚拟页使用无锁堆管理? */


/* 如果需要则应该自己去填充 */

/* 需要知道上一级负责的最大大小?以方便处理对齐? */
/* 还是按照size 自对齐? */

/* 怎么wf? */
/* 理论上只用不断增长即可，如果空间不够了怎么办? fork一份?然后尝试取而代之? */
/* 空间不够了可以unmmap掉 */
/* 还能怎么做? - append连接上去*/

/* 运行时间长了会不会导致碎片问题? mmap reserve可以让内核处理碎片问题 */
/* mmap size溢出后 */
/* 需要知道上一级最大对齐 */
#include <metalc/base/mlayer.h>

#include <metalc/mlos/mmap/unix_mmap.h>

/* 永远++直到用完 */

typedef struct mmapheap
{
	void *start_heap_ptr;
	void *cur_heap_ptr;
	void *end_heap_ptr;
} mmapheap_t;

/* 全局唯一 */
static mmapheap_t mmapheap;

#if (WORD_WIDTH == 64)
#define HEAPSTART_OFFST	 (SIZE_1G) /* 页表靠近, cache友好 */
#elif (WORD_WIDTH == 32)
#define HEAPSTART_OFFST	 (SIZE_256M)
#endif

#define MMAP_PRINT(...)		INFO(__VA_ARGS__)
// #define MMAP_PRINT(...)		dbg_printf(__VA_ARGS__)
static inline void mmapheap_init(size_t min_alignsize, mmapheap_t *l){
	/* 对齐方便映射巨页 */
	l->start_heap_ptr = (void *)align_up((size_t)os_get_heapstart(), min_alignsize);
	INFO("mmapheap start at %p\n", l->start_heap_ptr);
	/* 然后尝试对齐?(不用,申请时对齐) */
	l->cur_heap_ptr = l->start_heap_ptr;
}

// SAI void   _mmapheap_tinit_thread(const mlfunc_t * cont){
//	 mmapheap_t *l = (mmapheap_t *)mlcc_palloc(align_up(sizeof(mmapheap_t), HDEF_CACHE_LINE_BYTES));
//	 if (!l) fatal("mmapheap allocated failed");
//	 mmapheap_init(l);
//	 mtls_t *tls = mtls_get_init();
//	 tls->objs[cont->lidx] = l;
// }

SAI void _mmapheap_pinit(bool_t use_huge_page, size_t min_alignsize, size_t mmap_commit_size, size_t mmap_commit_size_max, const mlfunc_t * cont){ 
	PARAM_USED(cont);
	if (mmapheap.cur_heap_ptr == NULL_PTR) {
		
		mmapheap_init(min_alignsize, &mmapheap);
		
		size_t sz_get = 0;
		void *ptr = os_reserve_at_least_atheap(mmap_commit_size, mmap_commit_size_max, &sz_get, mmapheap.cur_heap_ptr, use_huge_page);
		if (mmapheap.cur_heap_ptr == NULL) fatal("mmapheap can not create\n");
		mmapheap.cur_heap_ptr = ptr;
		mmapheap.end_heap_ptr = (void *)((paddr_t)ptr + sz_get);
	}
	
}

SAI void _mmapheap_tinit(const mlfunc_t * cont){
	PARAM_USED(cont);
	// mmapheap_t *l;
	// if (!uniq_heap){
	//	 l = (mmapheap_t *)mlcc_palloc(align_up(sizeof(mmapheap_t), HDEF_CACHE_LINE_BYTES));
	//	 ASSUME(l);
	//	 mmapheap_init(l);
	//	 mtls_t *tls = mtls_get_init();
	//	 tls->objs[cont->lidx] = l;
	// }
}

SAI void *_mmapheap_alloc(bool_t use_huge_page, size_t min_alignsize, mreq_t *req, const mlfunc_t *cont){
	PARAM_USED(cont);
	mmapheap_t *l = &mmapheap;
	
	void *ret = NULL;
	size_t req_size = req->size;
	size_t align = req->align;
	/* */
	/* ASSERT(align >= PAGE_SIZE); */
	/* 对齐? */

	size_t align_size = req->size;
	if(min_alignsize < req->align){
		align_size += align;
	}else{
		align_size = align_up(align_size, min_alignsize);
		/* align = 0; */ /* 已经对齐 */
		align = min_alignsize;
	}

	/* TODO:这里应该按这个对齐 */
	// PARAM_USED(min_alignsize);
	// align_size = align_up(align_size, req->align);
	
	size_t align_mask = align - 1;
	size_t block_start = 0;
	if (l->cur_heap_ptr + align_size <= l->end_heap_ptr){
		block_start = (size_t)faa(&l->cur_heap_ptr, align_size);
		if (block_start + align_size <= (size_t)l->end_heap_ptr){
			/* 分配成功 尝试对齐*/
			ret = (void *)block_start;

			if (align){
				ret = (void *)((block_start + align_mask) & (~(align_mask)));
				align_size -= ((size_t)ret - block_start);
			}
			
			req->ptr = ret;
			req->size = align_size;
		}
	}
	if (!ret){
		/* 尝试自己mmap */
		INFO("running out of mapped memory, try mapping\n");
		if (block_start != 0){
			block_start = (size_t)faa(&l->cur_heap_ptr, align_size);
		}
		ret = __os_commit((void *)block_start, align_size, use_huge_page);
	}

	/* if (!ret) fatal("mmapheap no memory\n"); */
	/* 大于堆管理的大小直接返回 */

	/* 如果堆上满足直接返回 */
	req->ptr = ret;
	req->size = align_size;
	
	PARAM_USED(req_size); /* 调试使用 */
	MMAP_PRINT("[mmapheap] alloc %p, %lx, req_size %lx\n", ret, req->size, req_size);
	ASSERT(is_ptr_aligned(ret, align));
	return ret;
}

SAI bool_t _mmapheap_free(bool_t lazy_decommit, mreq_t *req, const mlfunc_t *cont){
	PARAM_USED(cont);
	mmapheap_t *l = &mmapheap;
	void *ptr = req->ptr;
	MMAP_PRINT("[mmapheap] free %p, %lx\n", req->ptr, req->size);
	return os_decommit_lazy(ptr, req->size, lazy_decommit);
}

/* 系统层不管理对象大小, 仅用于内部使用 */
SAI size_t _mmapheap_sizeof(mreq_t *req, const mlfunc_t * cont){
	PARAM_USED(req, cont);
	/* 由下面的大内存申请层管理 */
	req->size = MSIZEOF_ERR;
	return MSIZEOF_ERR;
}

#define DEF_MMAPHEAP_MLAYER(name, min_alignsize, mmap_size_advise_min, mmap_size_advise_max, use_huge_page, lazy_decommit) \
DEF_MLOS(name, 0)\
SAI void name##_pinit(const mlfunc_t * cont){ return _mmapheap_pinit(use_huge_page, min_alignsize, mmap_size_advise_min, mmap_size_advise_max, cont); }\
SAI void name##_tinit(const mlfunc_t * cont){ return _mmapheap_tinit(cont); }\
SAI void *name##_alloc(mreq_t *req, const mlfunc_t * cont){\
	return _mmapheap_alloc(use_huge_page, min_alignsize, req, cont);\
}\
SAI bool_t name##_free(mreq_t *req, const mlfunc_t * cont){\
	return _mmapheap_free(lazy_decommit, req, cont);\
}\
SAI size_t name##_sizeof(mreq_t *req, const mlfunc_t * cont){\
	return _mmapheap_sizeof(req, cont);\
}

